/*
 * Copyright (c) Zhongkehonglue Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Implementation of functions related to the DMC DEV module.
 * Author: wangchunyang
 * Create: 2025-05-23
 */

#include "dmcDevice.h"

#include "dmcConstant.h"
#include "dmcTime.h"
#include "upnp.h"
#include "dmcLog.h"
#include "dmcEnv.h"
#include "dmcCallback.h"

#ifdef LOG_TAG
#undef LOG_TAG
#endif
namespace DlnaDmc {
DmcDevice DmcDevice::instance;
constexpr dmc_int32 DEVICE_EXPIRED_TIME = 35;

dmc_int32 DmcDevice::AddDevice(std::shared_ptr<DevDescInfo> &deviceInfo)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    if (deviceInfo == nullptr) {
        LOGI("add device nullptr");
        return DMC_FAILURE;
    }
    auto it = devicesMap.find(deviceInfo->udn);
    if (it != devicesMap.end()) {
        LOGI("udn already exist, %{public}s",
             deviceInfo->udn.c_str());
        return DMC_SUCCESS;
    }
    deviceInfo->deviceId = GetNewDeviceId();
    deviceInfo->lastUpdateTime = GetCurrentTimeSeconds();
    LOGI("add device deviceid=%{public}d", deviceInfo->deviceId);
    devicesMap.emplace(deviceInfo->udn, deviceInfo);
    return DMC_SUCCESS;
}

dmc_int32 DmcDevice::DeleteDevice(const std::string &udn)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    auto it = devicesMap.find(udn);
    if (it == devicesMap.end()) {
        LOGI("device not found");
        return DMC_SUCCESS;
    }
    devicesMap.erase(udn);
    return DMC_SUCCESS;
}

dmc_int32 DmcDevice::DeleteByDeviceId(dmc_uint32 deviceId)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    for (auto it = devicesMap.begin(); it != devicesMap.end(); ) {
        if (it->second->deviceId == deviceId) {
            it = devicesMap.erase(it);
        } else {
            ++it;
        }
    }
    return DMC_SUCCESS;
}

dmc_int32 DmcDevice::ReSet()
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    devicesMap.clear();
    deviceIdGenerator = 1;
    return DMC_SUCCESS;
}

bool DmcDevice::DoesUdnExist(const std::string &udn)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    auto it = devicesMap.find(udn);
    if (it == devicesMap.end()) {
        return false;
    }
    return true;
}

dmc_int32 DmcDevice::GetDeviceIdByUdn(const std::string &udn, dmc_uint32 &deviceId)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    auto it = devicesMap.find(udn);
    if (it == devicesMap.end()) {
        LOGE("device not found, udn=",udn.c_str());
        return DMC_FAILURE;
    }
    deviceId = devicesMap[udn]->deviceId;
    return DMC_SUCCESS;
}

dmc_int32 DmcDevice::GetUdnByDeviceId(dmc_uint32 deviceId, std::string &udn)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    for (auto it = devicesMap.begin(); it != devicesMap.end(); ++it) {
        if (it->second->deviceId == deviceId) {
            udn = it->first;
            return DMC_SUCCESS;
        }
    }
    return DMC_FAILURE;
}

dmc_int32 DmcDevice::GetAllUdn(std::vector<std::string> &udnVec)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    for (auto it = devicesMap.begin(); it != devicesMap.end(); ++it) {
        udnVec.emplace_back(it->first);
    }
    return DMC_SUCCESS;
}

std::shared_ptr<DevDescInfo> DmcDevice::FindDeviceByDeviceId(dmc_uint32 deviceId)
{
    for(const auto& [key, value] : devicesMap) {
        if (value->deviceId == deviceId) {
            return value;
        }
    }
    return nullptr;
}

dmc_int32 DmcDevice::GetControlUrl(dmc_uint32 deviceId, const std::string &serviceType,
    std::string &controlUrl)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    auto devSptr = FindDeviceByDeviceId(deviceId);
    if (devSptr == nullptr) {
        LOGE("device id = %{public}ld not found", deviceId);
        return DMC_FAILURE;
    }
    for (auto &eachService : devSptr->serviceVec) {
        if (eachService.serviceType == serviceType) {
            controlUrl = eachService.controlUrl;
            break;
        }
    }
    if (controlUrl.empty()) {
        LOGE("control url is empty.");
        return DMC_FAILURE;
    }
    return DMC_SUCCESS;
}

dmc_int32 DmcDevice::GetDescUrl(dmc_uint32 deviceId, std::string &descUrl)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    auto devSptr = FindDeviceByDeviceId(deviceId);
    if (devSptr == nullptr) {
        LOGE("device id = %{public}ld not found", deviceId);
        return DMC_FAILURE;
    }
    descUrl = devSptr->descUrl;
    if (descUrl.empty()) {
        LOGE("desc url is empty.");
        return DMC_FAILURE;
    }
    return DMC_SUCCESS;
}

void DmcDevice::ResetHeartBeatCount(dmc_uint32 deviceId)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    auto devSptr = FindDeviceByDeviceId(deviceId);
    if (devSptr == nullptr) {
        LOGE("device id = %{public}ld not found", deviceId);
        return;
    }
    devSptr->heartBeatFailCnt = 0;
}

dmc_int32 DmcDevice::IncreaseHeartBeatCount(dmc_uint32 deviceId, dmc_int32 &failureCount)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    auto devSptr = FindDeviceByDeviceId(deviceId);
    if (devSptr == nullptr) {
        LOGE("device id = %{public}ld not found", deviceId);
        return DMC_FAILURE;
    }
    devSptr->heartBeatFailCnt++;
    failureCount = devSptr->heartBeatFailCnt;
    return DMC_SUCCESS;
}

dmc_uint32 DmcDevice::GetNewDeviceId()
{
    return deviceIdGenerator++;
}

dmc_int32 DmcDevice::UpdateLastUpdateTime(const std::string &udn)
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    auto it = devicesMap.find(udn);
    if (it == devicesMap.end()) {
        LOGE("udn not found");
        return DMC_FAILURE;
    }
    it->second->lastUpdateTime = GetCurrentTimeSeconds();
    return DMC_SUCCESS;
}

dmc_int32 DmcDevice::RemoveOfflineDevice()
{
    std::lock_guard<std::mutex> guard(devicesMtx);
    dmc_int64 currTime = GetCurrentTimeSeconds();
    for (auto it = devicesMap.begin(); it != devicesMap.end(); ) {
        dmc_uint32 deviceId = it->second->deviceId;
        if (currTime - it->second->lastUpdateTime > DEVICE_EXPIRED_TIME) {
            DmcCallback::GetInstance().ExecDeviceDownCb(deviceId);
            it = devicesMap.erase(it);
            LOGE("device id = %{public}ld has been deleted", deviceId);
        } else {
            LOGE("device id = %{public}ld is normal", deviceId);
            ++it;
        }
    }
    return DMC_SUCCESS;
}

void PeriodicSearchDevice()
{
    auto ret = UpnpSearchAsync(DmcEnv::GetInstance().GetClientHdl(), UPNP_SEARCH_TIME, "urn:schemas-upnp-org:device:MediaRenderer:1", nullptr);
    if (ret != UPNP_E_SUCCESS) {
        LOGE("DMR search request failed, ret code: %{public}ld", ret);
    }
}
}





